
#import "SavingsCalculator.h"
#import "Data.h"
#import "lampmeta.h"

#if DEBUG

#define CalcLog(fmt, arg) NSLog(fmt, arg)
#define CalcLog2(fmt, arg1, arg2) NSLog(fmt, arg1, arg2)

#else

#define CalcLog(fmt, arg) (void)0
#define CalcLog2(fmt, arg1, arg2) (void)0

#endif

#define kLampHoursPerYear 1000


#pragma mark - SavingsCalculator

@interface SavingsCalculator () {
    NSDictionary *_calculationLampTypes;
    NSInteger _calculationLumen; // Use the lumen max value for calculation
}

@end

@implementation SavingsCalculator

-(void)loadCalculationData
{
    NSString * const kLampTypes = @"types";
    NSString * const kCalculationHours = @"calculation_hours";
    NSString * const kEnergyPriceKWh = @"energy_price_kwh";

    NSError *error = nil;
    NSString *path = [[NSBundle mainBundle] pathForResource:@"savingscalculatordata" ofType:@"json"];
    NSData *jsonData = [NSData dataWithContentsOfFile:path];

    NSDictionary *rawData = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];
    NSAssert(error == nil, @"Error in JSON data: %@", error.localizedDescription);

    // setup calculation hours
    _calculationHours = [rawData[kCalculationHours] integerValue];
    _calculationYears = _calculationHours / kLampHoursPerYear;
    
    // setup energy price KWh
    _energyPriceKWh = [rawData[kEnergyPriceKWh] floatValue];

    // setup lamp types
    NSDictionary *rawLampTypes = rawData[kLampTypes];
    NSMutableDictionary *tempDict = [NSMutableDictionary dictionary];
    for (NSString *lampTypeKey in rawLampTypes) {
        tempDict[lampTypeKey] = [SavingsCalculatorLampType instanceFromDictionary:rawLampTypes[lampTypeKey]];
    }
    _calculationLampTypes = tempDict;
}

-(instancetype)initWithLampTypeWithKey:(NSString *)lampTypeKey effect:(CGFloat)effect
{
    self = [super init];
    if (self) {
        // Init calculation data
        [self loadCalculationData];
        
        _oldLampCalculation = [[SavingsCalculatorLampCalculation alloc] initWithSavingsCalculator:self];
        
        _oldLampCalculation.lampTypeKey = lampTypeKey;
        _oldLampCalculation.effect = effect;

        // Get parameters
        _oldLampCalculation.lampType = _calculationLampTypes[_oldLampCalculation.lampTypeKey];
        NSAssert1(_oldLampCalculation.lampType != nil, @"Type is not supported: %@", _oldLampCalculation.lampTypeKey);

        // Get Lumen
        NSDictionary *wattToLumenTable = [[Data sharedInstance] wattToLumenTableForLampTypeWithKey:_oldLampCalculation.lampTypeKey];
        NSString *effectKey = [NSString stringWithFormat:@"%.0f", effect];
        _luminousFluxRange = wattToLumenTable[effectKey];
        _calculationLumen = [_luminousFluxRange[1] integerValue]; // Use the lumen max value for calculation

        // Get price
        _oldLampCalculation.price = [self defaultPriceForLampType:_oldLampCalculation.lampType andLumen:_calculationLumen];
        
        CalcLog(@"\nOld lamp: %@\n", _oldLampCalculation);

        // Find substitutes
        NSArray *validSubstitutes = _oldLampCalculation.lampType.replaceWith;
        NSSet *substituteKeys = [_calculationLampTypes keysOfEntriesPassingTest:^BOOL(NSString *key, SavingsCalculatorLampType *item, BOOL *stop) {
            return [validSubstitutes containsObject:key] && _calculationLumen <= item.maxLumen;
        }];
        _substitutes = [_calculationLampTypes objectsForKeys:[substituteKeys allObjects] notFoundMarker:@""];
        _substitutes = [_substitutes sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"energyRank" ascending:YES]]];
                
        _substCalculation = [NSMutableArray array];
        for (SavingsCalculatorLampType *type in _substitutes) {
            CGFloat price = [self loadSavedPurchasePriceForLampType:type andLumen:_calculationLumen];
            if (!price) {
                price = [self defaultPriceForLampType:type andLumen:_calculationLumen];
            }
            SavingsCalculatorLampCalculation *calculation = [self calculationForLampType:type withPurchasePrice:price];
            [_substCalculation addObject:calculation];
        }
    }
    return self;    
}

-(float)defaultPriceForSubstituteAtIndex:(NSInteger)index
{
    SavingsCalculatorLampType *type = self.substitutes[index];
    return [self defaultPriceForLampType:type andLumen:_calculationLumen];
}

- (CGFloat) defaultPriceForLampType:(SavingsCalculatorLampType *)lampType andLumen:(NSInteger)lumen
{
    NSArray *defaultPrices = lampType.standardPrices; // price and "price valid up to x lumen" pairs
    CGFloat defaultPrice = [defaultPrices[0] floatValue];
    for (NSInteger i = 0; i < defaultPrices.count; i += 2) {
        defaultPrice = [defaultPrices[i] floatValue];
        NSInteger validUpToLumen = [defaultPrices[i + 1] integerValue];
        if (lumen <= validUpToLumen) {
            break;
        }
    }
    return defaultPrice;
}

-(void)setPurchasePrice:(float)price forSubstituteAtIndex:(NSInteger)index
{
    SavingsCalculatorLampType *type = self.substitutes[index];
    [self savePurchasePrice:price forLampType:type andLumen:_calculationLumen];
    self.substCalculation[index] = [self calculationForLampType:type withPurchasePrice:price];
}

-(void)resetPurchasePriceForSubstituteAtIndex:(NSInteger)index
{
    SavingsCalculatorLampType *type = self.substitutes[index];
    [self removeSavedPurchasePriceForLampType:type andLumen:_calculationLumen];

    CGFloat price = [self defaultPriceForLampType:type andLumen:_calculationLumen];
    self.substCalculation[index] = [self calculationForLampType:type withPurchasePrice:price];
}

-(float)purchasePriceForSubstituteAtIndex:(NSInteger)index
{
    return [self.substCalculation[index] price];
}

-(float)savingsForSubstituteAtIndex:(NSInteger)index
{
    return [self.substCalculation[index] totalSavings];
}

-(float)savingsForSubstituteAtIndex:(NSInteger)index usePurchasePrice:(float)price
{
    SavingsCalculatorLampType *type = self.substitutes[index];
    SavingsCalculatorLampCalculation *calculation = [self calculationForLampType:type withPurchasePrice:price];
    return calculation.totalSavings;
}

-(SavingsCalculatorLampCalculation *)calculationForLampType:(SavingsCalculatorLampType *)lampType withPurchasePrice:(float)aprice
{
    SavingsCalculatorLampCalculation *newLampCalculation = [[SavingsCalculatorLampCalculation alloc] initWithSavingsCalculator:self];
    newLampCalculation.lampType = lampType;
    newLampCalculation.price = aprice;
    newLampCalculation.effect = (self.oldLampCalculation.effect *
                                newLampCalculation.lampType.powerNeedRelativeClassic /
                                self.oldLampCalculation.lampType.powerNeedRelativeClassic);

    CalcLog(@"\nNew lamp: %@\n", newLampCalculation);
    
    return newLampCalculation;
}

- (NSString *)purchasePriceKeyForType:(SavingsCalculatorLampType *)type andLumen:(NSInteger)lumen
{
    return [NSString stringWithFormat:@"%@,%ld", type.name, (long)lumen];
}

#pragma mark - User Defaults
- (void)savePurchasePrice:(float)price forLampType:(SavingsCalculatorLampType *)type andLumen:(NSInteger)lumen
{
    NSString *key = [self purchasePriceKeyForType:type andLumen:lumen];
    [[NSUserDefaults standardUserDefaults] setFloat:price forKey:key];
}

- (void)removeSavedPurchasePriceForLampType:(SavingsCalculatorLampType *)type andLumen:(NSInteger)lumen
{
    NSString *key = [self purchasePriceKeyForType:type andLumen:lumen];
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
}

- (float)loadSavedPurchasePriceForLampType:(SavingsCalculatorLampType *)type andLumen:(NSInteger)lumen
{
    NSString *key = [self purchasePriceKeyForType:type andLumen:lumen];
    return [[NSUserDefaults standardUserDefaults] floatForKey:key];
}

@end
